package br.ufsc.ctc.ine5622.compilador;

import java.io.FileNotFoundException;
import java.io.IOException;

import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import br.ufsc.ctc.ine5622.compilador.gui.JanelaPrincipal;
import br.ufsc.ctc.ine5622.compilador.lex.LexicalError;
import br.ufsc.ctc.ine5622.compilador.lex.Lexico;
import br.ufsc.ctc.ine5622.compilador.lex.SemanticError;
import br.ufsc.ctc.ine5622.compilador.lex.Semantico;
import br.ufsc.ctc.ine5622.compilador.lex.Sintatico;
import br.ufsc.ctc.ine5622.compilador.lex.SyntaticError;
import br.ufsc.ctc.ine5622.compilador.lex.Token;
import br.ufsc.ctc.ine5622.compilador.arquivo.Extensao;
import br.ufsc.ctc.ine5622.compilador.arquivo.InterfaceArquivo;

/**
 * This class act as the controller for the application.
 * 
 * @author Paulo Pinho
 * @author William di Domenico
 */
public class Main {

	private JanelaPrincipal janela;
	private Lexico lex;
	private Sintatico sintatico;
	private Semantico semantico;
	private InterfaceArquivo arquivo;

	public Main() {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				janela = new JanelaPrincipal(Main.this);
			}
		});		
		lex = new Lexico();
		sintatico = new Sintatico();
		arquivo = new InterfaceArquivo();
	}

	private static void setLookAndFeel() {
		try {			
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (UnsupportedLookAndFeelException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Faz a análise léxica do programa.
	 * 
	 * @param codigo do programa
	 */
	public void analiseLexica(String codigo) {
		lex.setInput(codigo);
		Token token = null;
		try {
			while ((token = lex.nextToken()) != null) {
				System.out.println(token);
			}
			janela.showInfoMsg("Sem erros léxicos.", "Análisa léxica");
		} catch (LexicalError e) {
			janela.showErrorMsg("Erro léxico na posição "+ e.getPosition() + ": "+ e.getMessage() +" depois de '" + token.getLexeme() + "'", "Erro léxico");
			janela.setCarretPosition(e.getPosition());
		}
	}
	
	/**
	 * Faz a análise sintática do programa.
	 * 
	 * TODO Em erro, avisar ao usuário qual token ele esperava para estar correto.
	 * 
	 * @param codigo do programa
	 */
	public void analiseSintatica(String codigo) {
		analise(codigo, false);
	}
	
	/**
	 * Faz a análise semantica do programa.
	 * 
	 * TODO Em erro, avisar ao usuário qual token ele esperava para estar correto.
	 * 
	 * @param codigo do programa
	 */
	public void analiseSemantica(String codigo){
		analise(codigo, true);
	}
	
	/**
	 * Faz analise do codigo.
	 * @param codigo
	 * @param analiseSemantica flag para caso ele deva fazer analise semantica junto com a sintatica.
	 */
	private void analise(String codigo, boolean analiseSemantica){
		lex.setInput(codigo);
		semantico = new Semantico();
		try {
			semantico.setLigado(analiseSemantica);
			sintatico.parse(lex, semantico);
			if(analiseSemantica){
				janela.showInfoMsg("Sem erros semânticos.", "Análise semântica");
			}else{
				janela.showInfoMsg("Sem erros sintáticos.", "Análise sintática");
			}
		} catch (LexicalError e) {
			janela.showErrorMsg("Erro léxico na posição "+ e.getPosition() + ": "+ e.getMessage() + " antes de '" + sintatico.getCurrentToken().getLexeme() + "'", "Erro léxico");
			janela.setCarretPosition(e.getPosition());
		} catch (SyntaticError e) {
			janela.showErrorMsg("Erro sintático na posição "+ e.getPosition() + ": "+ e.getMessage() + " antes de '" + sintatico.getCurrentToken().getLexeme() + "'", "Erro sintático");
			janela.setCarretPosition(e.getPosition());
		} catch (SemanticError e) {
			janela.showErrorMsg("Erro semântico na posição "+ e.getPosition() + " antes de '" + sintatico.getCurrentToken().getLexeme() + "'" + ": "+ e.getMessage()  , "Erro semântico");
			janela.setCarretPosition(e.getPosition());
		}
	}

	
	/**
	 * Calls the InterfaceArquivo object for reading
	 * text from a file
	 *
	 * @param nomeArquivo
	 *            The filename, with preceding absolute path, to be read
	 * @return The content of the file
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public String ler(String nomeArquivo)
			throws FileNotFoundException, IOException {
		return arquivo.ler(nomeArquivo);
	}

	/**
	 * Calls the InterfaceArquivo object for saving
	 * a string to a file
	 * 
	 * @param conteudo
	 *            The content of the file, that is: the text to be saved
	 * @param nomeArquivo
	 *            The filename, with absolute path
	 * @param extensao
	 *            The extension of the file
	 * @return The filename, with absolute path
	 * @throws IOException
	 */
	public String salvar(String conteudo, String nomeArquivo,
			String extensao) throws IOException {
		Extensao ext = Extensao.valueOf(extensao);
		return arquivo.salvar(conteudo, nomeArquivo, ext);
	}
	
	/**
	 * Ends the program.
	 */
	public void endProgram() {
		System.exit(0);
	}

	public static void main(String[] args) {
		// "If you are going to set the L&F, you should do it as the very first step in your application."
		// Indeed won't work if done after the window frame is created.
		setLookAndFeel();
		new Main();
	}

}
